package org.oslims.client.presenter;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.oslims.client.AsyncCall;
import org.oslims.client.MainEventBus;
import org.oslims.client.service.AnalysisServiceAsync;
import org.oslims.client.service.SampleTypeServiceAsync;
import org.oslims.client.uicomponents.SortableHeader;
import org.oslims.client.uicomponents.WidgetUtils;
import org.oslims.client.uicomponents.editbox.EditSampleTypeBox;
import org.oslims.client.view.impl.SampleTypeEditorViewImpl;
import org.oslims.domain.Analysis;
import org.oslims.domain.SampleType;

import com.google.gwt.cell.client.ButtonCell;
import com.google.gwt.cell.client.FieldUpdater;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.cellview.client.TextColumn;
import com.google.gwt.user.client.ui.DockLayoutPanel;
import com.google.gwt.validation.client.InvalidConstraint;
import com.google.gwt.validation.client.interfaces.IValidator;
import com.google.gwt.view.client.ListView;
import com.google.gwt.view.client.Range;
import com.google.gwt.view.client.ListView.Delegate;
import com.mvp4g.client.annotation.InjectService;
import com.mvp4g.client.annotation.Presenter;
import com.mvp4g.client.presenter.BasePresenter;

@Presenter(view = SampleTypeEditorViewImpl.class)
public class SampleTypePresenter extends BasePresenter<SampleTypeEditorViewImpl, MainEventBus> {

	private SampleTypeServiceAsync sampleTypeService;
	private AnalysisServiceAsync analysisService;

	private IValidator<SampleType> sampleTypeValidator;
	private Map<Long, Analysis> analysisMap;

	private String currentSortProperty = "name";
	private Boolean currentSortDesc = false;

	public SampleTypePresenter() {
		sampleTypeValidator = GWT.create(SampleType.class);
		analysisMap = new HashMap<Long, Analysis>();
	}

	public void onShowSampleTypeManagement(DockLayoutPanel mainWidget) {

		WidgetUtils.removeCenterWidget(mainWidget);
		mainWidget.add(view.getViewWidget());

		refreshAnalysis();
		refreshData();
	}

	public void bind() {

		bindEditBox();

		addColumns();

		setDelegate();
	}

	private void setDelegate() {
		view.getTable().setDelegate(new Delegate<SampleType>() {

			@Override
			public void onRangeChanged(ListView<SampleType> listView) {

				final Range r = listView.getRange();

				sampleTypeService.getSampleTypes(r.getStart(), r.getLength(), currentSortProperty, currentSortDesc, new AsyncCall<List<SampleType>>() {

					@Override
					protected void onFailureE(Throwable caught) {
						eventBus.error(caught.getMessage());
					}

					@Override
					protected void onSuccessE(List<SampleType> result) {
						view.getTable().setData(r.getStart(), r.getLength(), result);
						view.getTable().redraw();
					}
				});
			}
		});
	}

	private void addColumns() {

		final SortableHeader nameHeader = new SortableHeader("Name", "name");
		nameHeader.setSorted(true);

		nameHeader.setUpdater(new ValueUpdater<String>() {

			@Override
			public void update(String value) {
				nameHeader.toggleReverseSort();
				currentSortProperty = nameHeader.getPropertyName();
				currentSortDesc = nameHeader.getReverseSort();
				view.getTable().refresh();
				view.getTable().refreshHeaders();
			}
		});

		view.getTable().addColumn(new TextColumn<SampleType>() {

			@Override
			public String getValue(SampleType object) {
				return object.getName();
			}
		}, nameHeader);

		view.getTable().addColumn(new TextColumn<SampleType>() {

			@Override
			public String getValue(SampleType object) {
				String analysis = "";

				for (Analysis s : object.getPossibleAnalysis()) {
					analysis += s.getName() + ", ";
				}

				if (analysis.length() > 0) {
					analysis = analysis.substring(0, analysis.length() - 2);
				}

				return analysis;
			}
		}, "Possible Analysis");

		Column<SampleType, String> editColumn = new Column<SampleType, String>(new ButtonCell()) {

			@Override
			public String getValue(SampleType object) {
				return "Edit";
			}
		};

		editColumn.setFieldUpdater(new FieldUpdater<SampleType, String>() {

			@Override
			public void update(int index, SampleType object, String value) {

				List<Analysis> selectedAnalysis = object.getPossibleAnalysis();
				List<Analysis> unselectedAnalysis = new ArrayList<Analysis>();

				for (Analysis a : analysisMap.values()) {
					if (!selectedAnalysis.contains(a)) {
						unselectedAnalysis.add(a);
					}
				}

				view.getEditSampleTypeBox().getAnalysisSelection().setItems(unselectedAnalysis, selectedAnalysis);

				view.getEditSampleTypeBox().showEdit(object);
			}
		});

		view.getTable().addColumn(editColumn);
	}

	private void bindEditBox() {
		view.getAddSampleTypeButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent arg0) {

				view.getEditSampleTypeBox().getAnalysisSelection().setItems(analysisMap.values(), new ArrayList<Analysis>());
				view.getEditSampleTypeBox().showNew();
			}
		});

		view.getEditSampleTypeBox().getCancelButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent arg0) {
				view.getEditSampleTypeBox().hide();
			}
		});

		view.getEditSampleTypeBox().getSaveButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent arg0) {
				if (view.confirmEdit("Do you want to save your changes?"))
					saveSampleType();
			}
		});
	}

	private void refreshData() {
		sampleTypeService.getSampleTypeCount(new AsyncCall<Integer>() {

			@Override
			protected void onFailureE(Throwable caught) {
				eventBus.error(caught.getMessage());
			}

			@Override
			protected void onSuccessE(Integer result) {
				view.getTable().setDataSize(result, true);

				if (result < view.getDefaultPageSize()) {
					view.getPager().setVisible(false);
					view.getTable().setPageSize(result);
				}
				view.getTable().refresh();
			}
		});
	}

	private void saveSampleType() {

		EditSampleTypeBox editBox = view.getEditSampleTypeBox();
		SampleType tempSampleType = editBox.getSampleType();

		if (tempSampleType == null) {
			tempSampleType = new SampleType();
		}

		tempSampleType.setName(editBox.getName().getValue());

		tempSampleType.getPossibleAnalysis().clear();

		for (Analysis a : editBox.getAnalysisSelection().getSelectedItems()) {
			tempSampleType.getPossibleAnalysis().add(a);
		}

		Set<InvalidConstraint<SampleType>> sampleTypeErrors = sampleTypeValidator.validate(tempSampleType);

		if (sampleTypeErrors.size() > 0) {
			for (InvalidConstraint<SampleType> ic : sampleTypeErrors) {
				editBox.showError(ic.getItemName(), ic.getMessage());
			}
			return;
		}

		sampleTypeService.saveOrUpdateSampleType(tempSampleType, new AsyncCall<Void>() {

			@Override
			public void onSuccessE(Void result) {

				view.getEditSampleTypeBox().hide();

				refreshData();
			}

			@Override
			public void onFailureE(Throwable error) {
				eventBus.error(error.getMessage());
			}
		});
	}

	private void refreshAnalysis() {

		analysisService.getAllAnalysis(new AsyncCall<List<Analysis>>() {

			@Override
			public void onSuccessE(List<Analysis> arg0) {
				analysisMap.clear();

				for (Analysis a : arg0) {
					analysisMap.put(a.getId(), a);
				}
			}

			@Override
			public void onFailureE(Throwable error) {
				eventBus.error(error.getMessage());
			}
		});
	}

	@InjectService
	public void setSampleTypeService(SampleTypeServiceAsync sampleTypeService) {
		this.sampleTypeService = sampleTypeService;
	}

	@InjectService
	public void setAnalysisService(AnalysisServiceAsync analysisService) {
		this.analysisService = analysisService;
	}
}
